home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / Delphi 3.0 / DATA.Z / report.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-01-29  |  32.3 KB  |  1,189 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Visual Component Library                 }
  5. {                                                       }
  6. {       Copyright (c) 1995,97 Borland International     }
  7. {                                                       }
  8. {*******************************************************}
  9.  
  10. unit Report;
  11.  
  12. {$Z+,R-}
  13.  
  14. interface
  15.  
  16. uses SysUtils, Windows, Classes, Controls, Forms, DDEMan, DB, Messages;
  17.  
  18. const
  19.   ctDBase = 2;
  20.   ctExcel = 3;
  21.   ctParadox = 4;
  22.   ctAscii = 5;
  23.   ctSqlServer = 6;
  24.   ctOracle = 7;
  25.   ctDB2 = 8;
  26.   ctNetSQL = 9;
  27.   ctSybase = 10;
  28.   ctBtrieve = 11;
  29.   ctGupta = 12;
  30.   ctIngres = 13;
  31.   ctWatcom = 14;
  32.   ctOcelot = 15;
  33.   ctTeraData = 16;
  34.   ctDB2Gupta = 17;
  35.   ctAS400 = 18;
  36.   ctUnify = 19;
  37.   ctQry = 20;
  38.   ctMinNative = 2;
  39.   ctMaxNative = 20;
  40.   ctODBCDBase = 40;
  41.   ctODBCExcel = 41;
  42.   ctODBCParadox = 42;
  43.   ctODBCSqlServer = 43;
  44.   ctODBCOracle = 44;
  45.   ctODBCDB2 = 45;
  46.   ctODBCNetSql = 46;
  47.   ctODBCSybase = 47;
  48.   ctODBCBtrieve = 48;
  49.   ctODBCGupta = 49;
  50.   ctODBCIngres = 50;
  51.   ctODBCDB2Gupta = 51;
  52.   ctODBCTeraData = 52;
  53.   ctODBCAS400 = 53;
  54.   ctODBCDWatcom = 54;
  55.   ctODBCDefault = 55;
  56.   ctODBCUnify = 56;
  57.   ctMinODBC = 40;
  58.   ctMaxODBC = 56;
  59.   ctIDAPIStandard = 60;
  60.   ctIDAPIParadox = 61;
  61.   ctIDAPIDBase = 62;
  62.   ctIDAPIAscii = 63;
  63.   ctIDAPIOracle = 64;
  64.   ctIDAPISybase = 65;
  65.   ctIDAPINovSql = 66;
  66.   ctIDAPIInterbase = 67;
  67.   ctIDAPIIBMEE = 68;
  68.   ctIDAPIDB2 = 69;
  69.   ctIDAPIInformix = 70;
  70.   ctMinIDAPI = 60;
  71.   ctMaxIDAPI = 70;
  72.  
  73. type
  74.   EReportError = class(Exception);
  75.   TReportManager = class;
  76.   TLaunchType = (ltDefault, ltRunTime, ltDesignTime);
  77.  
  78.   TReport = class(TComponent)
  79.   private
  80.     FOwner: TReportManager;
  81.     FReportName: string;
  82.     FReportDir: string;
  83.     FNumCopies: Word;
  84.     FStartPage: Word;
  85.     FEndPage: Word;
  86.     FMaxRecords: Word;
  87.     FRunTime: Boolean;
  88.     FStartedApp: Boolean;
  89.     FAutoUnload: Boolean;
  90.     FInitialValues: TStrings;
  91.     FVersionMajor: Integer;
  92.     FVersionMinor: Integer;
  93.     FReportHandle: HWND;
  94.     FPreview: Boolean;
  95.     FLaunchType: TLaunchType;
  96.     function GetBusy: Boolean;
  97.     function GetInitialValues: TStrings;
  98.     function GetReportHandle: HWND;
  99.     procedure RunApp;
  100.     procedure StartApplication;
  101.     function ReportActive: Boolean;
  102.     function RunReport: Integer;
  103.     procedure SetInitialValues(Value: TStrings);
  104.     function UseRunTime: Boolean;
  105.   protected
  106.     procedure Notification(AComponent: TComponent;
  107.       Operation: TOperation); override;
  108.   public
  109.     constructor Create(AOwner: TComponent); override;
  110.     destructor Destroy; override;
  111.     function CloseApplication(ShowDialogs: Boolean): Integer;
  112.     function CloseReport(ShowDialogs: Boolean): Integer;
  113.     function Connect(ServerType: Word; const ServerName,
  114.       UserName, Password, DatabaseName: string): Integer;
  115.     function Print: Integer;
  116.     function RecalcReport: Integer;
  117.     function Run: Integer;
  118.     function RunMacro(const Macro: string): Integer;
  119.     function SetVariable(const Name, Value: string): Integer;
  120.     function SetVariableLines(const Name: string; Value: TStrings): Integer;
  121.     property ReportHandle: HWND read FReportHandle;
  122.     property Busy: Boolean read GetBusy;
  123.     property VersionMajor: Integer read FVersionMajor;
  124.     property VersionMinor: Integer read FVersionMinor;
  125.   published
  126.     property ReportName: string read FReportName write FReportName;
  127.     property ReportDir: string read FReportDir write FReportDir;
  128.     property PrintCopies: Word read FNumCopies write FNumCopies default 1;
  129.     property StartPage: Word read FStartPage write FStartPage default 1;
  130.     property EndPage: Word read FEndPage write FEndPage default 9999;
  131.     property MaxRecords: Word read FMaxRecords write FMaxRecords default 0;
  132.     property AutoUnload: Boolean read FAutoUnload write FAutoUnload default False;
  133.     property InitialValues: TStrings read GetInitialValues write SetInitialValues;
  134.     property Preview: Boolean read FPreview write FPreview default False;
  135.     property LaunchType: TLaunchType read FLaunchType write FLaunchType default ltDefault;
  136.   end;
  137.  
  138. { TReportManager }
  139.  
  140.   TCallType = (ctNone, ctDesignId, ctExecuteSQL, ctEndSQL,
  141.     ctGetError, ctGetTableList, ctGetColumnList, ctGetNext, ctGetMemo);
  142.  
  143.   PCallInfo = ^TCallInfo;
  144.   TCallInfo = record
  145.     ProcessId: THandle;
  146.     CallType: TCallType;
  147.     ErrorCode: Bool;
  148.     Data: record end;
  149.   end;
  150.  
  151.   PRSDateTime= ^TRSDateTime;
  152.   TRSDateTime = record
  153.     Year: Word;
  154.     Month: Word;
  155.     Day: Word;
  156.     Hour: Word;
  157.     Min: Word;
  158.     Sec: Word;
  159.     MSec: Word;
  160.   end;
  161.  
  162.   PDataElement = ^TDataElement;
  163.   TDataElement = packed record
  164.     FieldType: Integer;
  165.     ColumnName: array[0..31] of char;
  166.     FieldLength: Word;
  167.     Null: Bool;
  168.     Data: record end;
  169.   end;
  170.  
  171.   PExecInfo = ^TExecInfo;
  172.   TExecInfo = record
  173.     DataSet: TDataSet;
  174.     MoreRecords: Bool;
  175.     NumCols: Word;
  176.   end;
  177.  
  178.   PStartExecInfo = ^TStartExecInfo;
  179.   TStartExecInfo = record
  180.     StmtIndex: Integer;
  181.     StmtName: array[0..19] of char;
  182.     MemoName: array[0..19] of char;
  183.     TableName: array[0..63] of char;
  184.   end;
  185.  
  186.   PMemoStruct = ^TMemoStruct;
  187.   TMemoStruct = record
  188.     DataSet: TDataSet;
  189.     Index: Integer;
  190.     ColumnName: array[0..31] of char;
  191.     Pos: Integer;
  192.   end;
  193.  
  194.   PSQLStruct = ^TSQLStruct;
  195.   TSQLStruct = record
  196.     DataSet: TDataSet;
  197.     Index: Integer;
  198.   end;
  199.  
  200.   TReportManager = class(TComponent)
  201.   private
  202.     FReports: TList;
  203.     FDataSets: TList;
  204.     FHandle: HWND;
  205.     FLastError: string;
  206.     FUpdated: Boolean;
  207.     procedure ServerProc(Value: PCallInfo);
  208.     procedure WndProc(var Message: TMessage);
  209.   public
  210.     constructor Create(AOwner: TComponent); override;
  211.     destructor Destroy; override;
  212.     procedure Add(Value: TReport);
  213.     procedure AddDataSet(Root: TComponent);
  214.     procedure Clear;
  215.     function EndSQL(SQLStruct: PSQLStruct): Bool;
  216.     function ExecuteSQL(ExecInfo: PExecInfo;
  217.       StartExecInfo: PStartExecInfo): Bool;
  218.     function GetColumnList(Buffer: PChar): Bool;
  219.     function GetData(DataSet: TDataSet; pStmtMem: PDataElement): Bool;
  220.     function GetDataSet(Index: Integer): TDataSet;
  221.     function GetDataSetByName(Value: string): TDataSet;
  222.     function GetDataSets: TList;
  223.     function GetMemo(MemoStruct: PMemoStruct): Bool;
  224.     function GetNext(SQLStruct: PSQLStruct; var MoreData: Bool): Bool;
  225.     function GetReport(Index: Integer): TReport;
  226.     procedure GetTableList(Buffer: PChar);
  227.     procedure Remove(Value: TReport);
  228.     procedure UpdateDataSets;
  229.     function ValidDataType(Value: TFieldType): Boolean;
  230.     property DataSets: TList read GetDataSets;
  231.     property Reports: TList read FReports;
  232.     property DataSet[Index: Integer]: TDataSet read GetDataSet;
  233.     property Handle: HWND read FHandle;
  234.     property Report[Index: Integer]: TReport read GetReport;
  235. {$WARNINGS OFF}
  236.     property Updated: Boolean read FUpdated;
  237. {$WARNINGS ON}
  238.   end;
  239.  
  240. implementation
  241.  
  242. uses DBConsts, IniFiles, Registry;
  243.  
  244. const
  245.   RSAPI = 'rs_api.dll';
  246.   RS_SUCCESS = 0;
  247.   RS_BUSY = 1;
  248.   DesignName = 'ReportSmith';
  249.   RunName = 'RS_RUNTIME';
  250.   ReportClassName: string = 'OwlWindow';
  251.   DesignExeName = 'RptSmith.EXE';
  252.   RunExeName = 'RS_Run.EXE';
  253.   StatementBuffer = $FFFF;
  254.   MemoBuffer = $8000;
  255.   SRegBaseKey = '\software\borland\ReportSmith\3.00';
  256.  
  257. type
  258.   TServerProc = function(var Data: Integer): Bool stdcall;
  259.   TStmtStruct = record
  260.     StmtHandle: THandle;
  261.     StmtMem: Pointer;
  262.     MemoHandle: THandle;
  263.     MemoMem: Pointer;
  264.   end;
  265.  
  266. var
  267.   StartEvent: THandle;
  268.   SyncEvent: THandle;
  269.   SharedMem: Pointer;
  270.   ProcessId: Integer;
  271.   ReportManager: TReportManager;
  272.   StmtHandles: array[0..9] of TStmtStruct;
  273.   DriverHandle: THandle;
  274.   APIDriverHandle: THandle;
  275.   InitObjects: function(var StartEvent: THandle; var SyncEvent: THandle;
  276.     var SharedMem: Pointer; ThreadFunc: TThreadStartRoutine):Bool stdcall;
  277.   GetThread: function: THandle stdcall;
  278.   RS_PrintReport: function(StartingPage, EndingPage: Integer; Device, Port, Driver: PChar;
  279.     Copies: Integer): Integer; stdcall;
  280.   RS_SetRepVar: function(Name, Value: PChar): Integer; stdcall;
  281.   RS_Recalc: function: Integer; stdcall;
  282.   RS_CloseReport: function(Close: Integer): Integer; stdcall;
  283.   RS_CloseRS: function(Close: Integer): Integer; stdcall;
  284.   RS_SetRecordLimit: function(Limit: Integer): Integer; stdcall;
  285.   RS_LoadReport: function(FileName, Arguments: PChar; DraftMode,
  286.     RunReport: Bool): Integer; stdcall;
  287.   RS_ByteVersion: function(var Major, Minor: Integer): Word; stdcall;
  288.   RS_Connect: function(ServerType: Integer; const Server, UserId, Password,
  289.     Database: PChar): Integer; stdcall;
  290.   RS_IsBusy: function: Bool; stdcall;
  291.   RS_RunMacro: function(Macro: PChar): Integer; stdcall;
  292.   RS_IsReportSmithPresent: function: Bool; stdcall;
  293.   RS_Initiate: function(RunTime: Bool): Integer; stdcall;
  294.   RS_RegisterCallBack: function(Value: Pointer): Integer; stdcall;
  295.  
  296. function AsyncCallback: Boolean;
  297. var
  298.   Msg: TMsg;
  299. begin
  300.   if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then
  301.   begin
  302.     with Application do
  303.     begin
  304.       HandleMessage;
  305.       Result := Terminated;
  306.      end;
  307.   end else
  308.     Result := False
  309. end;
  310.  
  311. function GetRootDir(RunTime: Boolean): string;
  312. var
  313.   Value: string;
  314. begin
  315.   if RunTime then
  316.     Value := SRptRunTimeValue else
  317.     Value := SRptDesignTimeValue;
  318.   with TRegistry.Create do
  319.   try
  320.     RootKey := HKEY_LOCAL_MACHINE;
  321.     OpenKey(SRegBaseKey, True);
  322.     Result := ReadString(Value);
  323.   finally
  324.     Free;
  325.   end;
  326. end;
  327.  
  328. function APIDriverLoaded: Boolean;
  329. begin
  330.   Result := APIDriverHandle >= HINSTANCE_ERROR;
  331. end;
  332.  
  333. function InitAPIDriver: Boolean;
  334. var
  335.   OldError: Word;
  336.   Path: string;
  337. begin
  338.   OldError := SetErrorMode(SEM_NOOPENFILEERRORBOX);
  339.   try
  340.     Path := GetRootDir(False);
  341.     if Path = '' then
  342.       Path := GetRootDir(True);
  343.     if (Path <> '') and (Path[Length(Path)] <> '\') then
  344.       Path := Path + '\';
  345.     Path := Path + RSAPI;
  346.     APIDriverHandle := LoadLibrary(PChar(Path));
  347.     if APIDriverLoaded then
  348.     begin
  349.       @RS_PrintReport := GetProcAddress(APIDriverHandle, 'RS_PrintReport');
  350.       @RS_SetRepVar := GetProcAddress(APIDriverHandle, 'RS_SetRepVar');
  351.       @RS_Recalc := GetProcAddress(APIDriverHandle, 'RS_Recalc');
  352.       @RS_CloseReport := GetProcAddress(APIDriverHandle, 'RS_CloseReport');
  353.       @RS_CloseRS := GetProcAddress(APIDriverHandle, 'RS_CloseRS');
  354.       @RS_SetRecordLimit := GetProcAddress(APIDriverHandle, 'RS_SetRecordLimit');
  355.       @RS_LoadReport := GetProcAddress(APIDriverHandle, 'RS_LoadReport');
  356.       @RS_ByteVersion := GetProcAddress(APIDriverHandle, 'RS_ByteVersion');
  357.       @RS_Connect := GetProcAddress(APIDriverHandle, 'RS_Connect');
  358.       @RS_IsBusy := GetProcAddress(APIDriverHandle, 'RS_IsAPIBusy');
  359.       @RS_RunMacro := GetProcAddress(APIDriverHandle, 'RS_RunMacroCode');
  360.       @RS_IsReportSmithPresent := GetProcAddress(APIDriverHandle, 'RS_IsReportSmithPresent');
  361.       @RS_Initiate := GetProcAddress(APIDriverHandle, 'RS_InitiateAPI');
  362.       @RS_RegisterCallBack := GetProcAddress(APIDriverHandle, 'RS_RegisterWaitLoopCallback');
  363.     end
  364.     else APIDriverHandle := 1;
  365.   finally
  366.     SetErrorMode(OldError);
  367.   end;
  368.   Result := APIDriverLoaded;
  369. end;
  370.  
  371. function DriverLoaded: Boolean;
  372. begin
  373.   Result := DriverHandle >= HINSTANCE_ERROR;
  374. end;
  375.  
  376. function InitDriver: Boolean;
  377. const
  378.   RSDriverName = 'RS_DELPH.DLL';
  379. var
  380.   OldError: Word;
  381.   Path: string;
  382. begin
  383.   OldError := SetErrorMode(SEM_NOOPENFILEERRORBOX);
  384.   try
  385.     Path := GetRootDir(False);
  386.     if Path = '' then
  387.       Path := GetRootDir(True);
  388.     if (Path <> '') and (Path[Length(Path)] <> '\') then
  389.       Path := Path + '\';
  390.     Path := Path + RSDriverName;
  391.     DriverHandle := LoadLibrary(PChar(Path));
  392.     if DriverLoaded then
  393.     begin
  394.       @InitObjects := GetProcAddress(DriverHandle, 'InitObjects');
  395.       @GetThread := GetProcAddress(DriverHandle, 'GetThread');
  396.     end
  397.     else DriverHandle := 1;
  398.   finally
  399.     SetErrorMode(OldError);
  400.   end;
  401.   Result := DriverLoaded;
  402. end;
  403.  
  404. procedure RaiseError(const Message: string);
  405. begin
  406.   raise EReportError.Create(Message);
  407. end;
  408.  
  409. procedure GetDecodedDateTime(DateTime: TDateTime; var Value: TRSDateTime);
  410. begin
  411.   with Value do
  412.   begin
  413.     DecodeDate(DateTime, Year, Month, Day);
  414.     DecodeTime(DateTime, Hour, Min, Sec, MSec);
  415.   end;
  416. end;
  417.  
  418. procedure CleanUpStmt(Value: TStmtStruct);
  419. begin
  420.   with Value do
  421.   begin
  422.     if StmtHandle <> 0 then
  423.     begin
  424.       if StmtMem <> nil then UnmapViewOfFile(StmtMem);
  425.       StmtMem := nil;
  426.       CloseHandle(StmtHandle);
  427.       StmtHandle := 0;
  428.     end;
  429.     if MemoHandle <> 0 then
  430.     begin
  431.       if MemoMem <> nil then UnmapViewOfFile(MemoMem);
  432.       MemoMem := nil;
  433.       CloseHandle(MemoHandle);
  434.       MemoHandle := 0;
  435.     end;
  436.   end;
  437. end;
  438.  
  439. { TReport }
  440.  
  441. constructor TReport.Create(AOwner: TComponent);
  442. begin
  443.   inherited Create(AOwner);
  444.   ReportManager.Add(Self);
  445.   PrintCopies := 1;
  446.   StartPage := 1;
  447.   EndPage := 9999;
  448.   MaxRecords := 0;
  449.   FInitialValues := TStringList.Create;
  450.   LaunchType := ltDefault;
  451. end;
  452.  
  453. destructor TReport.Destroy;
  454. begin
  455.   ReportManager.Remove(Self);
  456.   if FRunTime and FStartedApp then CloseApplication(True);
  457.   FInitialValues.Free;
  458.   inherited Destroy;
  459. end;
  460.  
  461. procedure TReport.SetInitialValues(Value: TStrings);
  462. begin
  463.   FInitialValues.Assign(Value);
  464. end;
  465.  
  466. function TReport.GetInitialValues: TStrings;
  467. begin
  468.   Result := FInitialValues;
  469. end;
  470.  
  471. function TReport.SetVariable(const Name, Value: string): Integer;
  472. begin
  473.   if not Busy then
  474.   begin
  475.     Result := RS_SetRepVar(PChar(Name), PChar(Value));
  476.   end else
  477.     Result := RS_BUSY;
  478. end;
  479.  
  480. function TReport.SetVariableLines(const Name: string; Value: TStrings): Integer;
  481. var
  482.   Buffer, StrEnd: PChar;
  483.   BufLen: Word;
  484.   I, L, Count: Integer;
  485.   Temp: array[0..255] of Char;
  486.   S: string;
  487. begin
  488.   if not Busy then
  489.   begin
  490.     BufLen := 3;
  491.     for I := 0 to Value.Count - 1 do
  492.     begin
  493.       L := Length(Value[I]) + 2;
  494.       if L > 65520 - BufLen then Break;
  495.       Inc(BufLen, L);
  496.     end;
  497.     Buffer := AllocMem(BufLen);
  498.     try
  499.       StrEnd := StrECopy(Buffer, '"');
  500.       Count := Value.Count - 1;
  501.       for I := 0 to Count do
  502.       begin
  503.         StrCopy(Temp, PChar(Value[I]));
  504.         StrEnd := StrECopy(StrEnd, Temp);
  505.         if I <> Count then StrEnd := StrECopy(StrEnd, ' ');
  506.       end;
  507.       Buffer[StrLen(Buffer)] := '"';
  508.       S := Buffer;
  509.       Result := RS_SetRepVar(PChar(S), nil);
  510.     finally
  511.       FreeMem(Buffer, BufLen);
  512.     end;
  513.   end else
  514.     Result := RS_BUSY;
  515. end;
  516.  
  517. function TReport.RecalcReport: Integer;
  518. begin
  519.   if not Busy then
  520.     Result := RS_Recalc else
  521.     Result := RS_BUSY;
  522. end;
  523.  
  524. function TReport.ReportActive: Boolean;
  525. begin
  526.   Result := (ReportHandle <> 0) and (@RS_IsReportSmithPresent <> nil) and
  527.     RS_IsReportSmithPresent;
  528. end;
  529.  
  530. function TReport.UseRunTime: Boolean;
  531. begin
  532.   Result := (LaunchType = ltRunTime) or
  533.     ((LaunchType = ltDefault) and not (csDesigning in ComponentState));
  534. end;
  535.  
  536. function TReport.Print: Integer;
  537. begin
  538.   if not Busy then
  539.     Result := RS_PrintReport(StartPage, EndPage, nil, nil, nil, PrintCopies) else
  540.     Result := RS_BUSY;
  541. end;
  542.  
  543. procedure TReport.StartApplication;
  544. var
  545.   ExeName: string;
  546.   ExePath: string;
  547.   StartupInfo: TStartupInfo;
  548.   ProcessInfo: TProcessInformation;
  549. begin
  550.   ExePath := GetRootDir(FRunTime);
  551.   if FRunTime then
  552.     ExeName := RunExeName else
  553.     ExeName := DesignExeName;
  554.   if (ExePath <> '') and (AnsiLastChar(ExePath)^ <> '\') then
  555.     ExePath := ExePath + '\';
  556.   ExeName := ExePath + ExeName;
  557.   FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
  558.   with StartupInfo do
  559.   begin
  560.     cb := SizeOf(TStartupInfo);
  561.     dwFlags := STARTF_USESHOWWINDOW or STARTF_FORCEONFEEDBACK;
  562.     if not FRunTime or Preview then wShowWindow := SW_SHOWNORMAL
  563.     else wShowWindow := SW_SHOWMINNOACTIVE;
  564.   end;
  565.   FStartedApp := CreateProcess(PChar(ExeName), nil, nil, nil, False,
  566.     NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo);
  567.   if FStartedApp then
  568.   with ProcessInfo do
  569.   begin
  570.     WaitForInputIdle(hProcess, INFINITE);
  571.     CloseHandle(hThread);
  572.     CloseHandle(hProcess);
  573.     FReportHandle := GetReportHandle;
  574.   end else
  575.     raise Exception.Create(Format(SRptLoadFailed, [ExeName]))
  576. end;
  577.  
  578. function TReport.CloseReport(ShowDialogs: Boolean): Integer;
  579. begin
  580.   if not RS_IsBusy then
  581.   begin
  582.     if ReportActive then
  583.       Result := RS_CloseReport(Ord(ShowDialogs))
  584.     else Result := RS_SUCCESS;
  585.   end else
  586.     Result := RS_BUSY;
  587. end;
  588.  
  589. function TReport.Connect(ServerType: Word; const ServerName,
  590.   UserName, Password, DatabaseName: string): Integer;
  591. begin
  592.   Result := 0;
  593.   if not Busy then
  594.   begin
  595.     if ((ServerType >= ctMinNative) and (ServerType <= ctMaxNative)) or
  596.       ((ServerType >= ctMinODBC) and (ServerType <= ctMaxODBC)) or
  597.       ((ServerType >= ctMinIDAPI) and (ServerType <= ctMaxIDAPI)) then
  598.       Result := RS_Connect(ServerType, PChar(ServerName), PChar(UserName),
  599.         PChar(Password), PChar(DatabaseName))
  600.     else RaiseError(SInvalidServer);
  601.   end else
  602.     Result := RS_BUSY;
  603. end;
  604.  
  605. function TReport.CloseApplication(ShowDialogs: Boolean): Integer;
  606. begin
  607.   if not RS_IsBusy then
  608.   begin
  609.     if ReportActive then
  610.     begin
  611.       Result := RS_CloseRS(Ord(ShowDialogs));
  612.       if Result = RS_SUCCESS then
  613.       begin
  614.         FStartedApp := False;
  615.         FReportHandle := 0;
  616.       end;
  617.     end
  618.     else Result := RS_SUCCESS;
  619.   end else
  620.     Result := RS_BUSY;
  621. end;
  622.  
  623. function TReport.GetReportHandle: HWND;
  624. var
  625.   S: string;
  626. begin
  627.   if FRunTime then S := RunName
  628.   else S := DesignName;
  629.   Result := FindWindow(PChar(ReportClassName), PChar(S));
  630. end;
  631.  
  632. function TReport.GetBusy: Boolean;
  633. begin
  634.   if not ReportActive then RunApp;
  635.   Result := RS_IsBusy;
  636. end;
  637.  
  638. function TReport.RunMacro(const Macro: string): Integer;
  639. begin
  640.   if not Busy then
  641.   begin
  642.     if Macro <> '' then
  643.       Result := RS_RunMacro(PChar(Macro)) else
  644.       Result := RS_SUCCESS;
  645.   end else
  646.     Result := RS_BUSY;
  647. end;
  648.  
  649. procedure TReport.RunApp;
  650. var
  651.   AppName: string;
  652. begin
  653.   if not APIDriverLoaded then
  654.     raise Exception.Create(Format(SUnableToLoadAPIDLL, [RSAPI]));
  655.   if not ReportActive and not RS_IsBusy then
  656.   begin
  657.     FRunTime := UseRunTime;
  658.     FReportHandle := GetReportHandle;
  659.     if ReportHandle = 0 then StartApplication;
  660.     RS_Initiate(FRunTime);
  661.     if FRunTime then AppName := RunName
  662.     else AppName := DesignName;
  663.     if RS_ByteVersion(FVersionMajor, FVersionMinor) <> RS_SUCCESS then
  664.       raise Exception.CreateFmt(SCannotGetVersionInfo, [AppName]);
  665.     if VersionMajor = 0 then
  666.     begin
  667.       if FStartedApp then CloseApplication(False);
  668.       raise Exception.Create(SIncorrectVersion);
  669.     end;
  670.   end;
  671. end;
  672.  
  673. function TReport.Run: Integer;
  674. begin
  675.   Result := RunReport;
  676.   if FRunTime and FStartedApp and
  677.     AutoUnload and not Preview then CloseApplication(True);
  678. end;
  679.  
  680. function TReport.RunReport: Integer;
  681. var
  682.   Path, FileName: string;
  683.   Temp: array[0..255] of Char;
  684.   Buffer, StrEnd: PChar;
  685.   BufLen: Word;
  686.   I, L, Count: Integer;
  687.   S: string;
  688. begin
  689.   if not Busy then
  690.   begin
  691.     Result := RS_SetRecordLimit(MaxRecords);
  692.     if Result = RS_SUCCESS then
  693.     begin
  694.       Path := ReportDir;
  695.       if (Path <> EmptyStr) and (AnsiLastChar(Path)^ <> '\') then
  696.         Path := Path + '\';
  697.       FileName := ReportName;
  698.       if (FileName <> '') and (Pos('.', FileName) = 0) then
  699.         FileName := FileName + '.rpt';
  700.       if FileName <> '' then
  701.       begin
  702.         FileName := Path + FileName;
  703.         if not FileExists(FileName) then
  704.           raise Exception.CreateFmt(SNoFile, [FileName]);
  705.         BufLen := 3;
  706.         for I := 0 to FInitialValues.Count - 1 do
  707.         begin
  708.           L := Length(FInitialValues[I]) + 2;
  709.           if L > 65520 - BufLen then Break;
  710.           Inc(BufLen, L);
  711.         end;
  712.         Buffer := AllocMem(BufLen);
  713.         try
  714.           StrEnd := StrECopy(Buffer, '"');
  715.           Count := FInitialValues.Count - 1;
  716.           for I := 0 to Count do
  717.           begin
  718.             StrCopy(Temp, PChar(FInitialValues[I]));
  719.             StrEnd := StrECopy(StrEnd, Temp);
  720.             if (I <> Count) and (Pos('>', FInitialValues[I]) > 0) then
  721.               StrEnd := StrECopy(StrEnd, ', ');
  722.           end;
  723.           Buffer[StrLen(Buffer)] := '"';
  724.           S := Buffer;
  725.           FmtStr(S, '%s,"#%x"', [S, ProcessId]);
  726.           Result := RS_LoadReport(PChar(FileName), PChar(S), False, True);
  727.         finally
  728.           FreeMem(Buffer, BufLen);
  729.         end;
  730.         if (Result = RS_SUCCESS) and FRunTime and not Preview then
  731.           Result := Print;
  732.       end;
  733.     end;
  734.   end else
  735.     Result := RS_BUSY;
  736. end;
  737.  
  738. procedure TReport.Notification(AComponent: TComponent;
  739.   Operation: TOperation);
  740. begin
  741.   inherited Notification(AComponent, Operation);
  742.   if AComponent is TDataSet then ReportManager.FUpdated := False;
  743. end;
  744.  
  745. { TReportManager }
  746.  
  747. constructor TReportManager.Create(AOwner: TComponent);
  748. begin
  749.   inherited Create(AOwner);
  750.   FReports := TList.Create;
  751.   FDataSets := TList.Create;
  752.   FHandle := AllocateHWnd(WndProc);
  753. end;
  754.  
  755. destructor TReportManager.Destroy;
  756. begin
  757.   Clear;
  758.   Reports.Free;
  759.   FDataSets.Free;
  760.   DeallocateHWnd(FHandle);
  761.   inherited Destroy;
  762. end;
  763.  
  764. procedure TReportManager.Clear;
  765. begin
  766.   while Reports.Count > 0 do TReport(Reports.Last).Free;
  767. end;
  768.  
  769. procedure TReportManager.WndProc(var Message: TMessage);
  770. begin
  771.   if Message.Msg = $7F00 then
  772.   begin
  773.     ServerProc(PCallInfo(SharedMem));
  774.   end
  775.   else with Message do
  776.     Result := DefWindowProc(FHandle, Msg, WParam, LParam);
  777. end;
  778.  
  779. procedure TReportManager.ServerProc(Value: PCallInfo);
  780. var
  781.   pData: Pointer;
  782. begin
  783.   pData := @Value^.Data;
  784.   with Value^ do
  785.   begin
  786.     ErrorCode := False;
  787.     case CallType of
  788.       ctExecuteSQL: ErrorCode := not ExecuteSQL(PExecInfo(pData),
  789.         PStartExecInfo(pData));
  790.       ctEndSQL: ErrorCode := not EndSQL(PSQLStruct(pData));
  791.       ctGetTableList: GetTableList(PChar(pData));
  792.       ctGetColumnList: ErrorCode := not GetColumnList(PChar(pData));
  793.       ctGetNext: ErrorCode := not GetNext(PSQLStruct(pData), Bool(pData^));
  794.       ctGetMemo: ErrorCode := not GetMemo(PMemoStruct(pData));
  795.       ctGetError: StrCopy(PChar(pData), PChar(FLastError));
  796.     end;
  797.   end;
  798. end;
  799.  
  800. procedure TReportManager.Add(Value: TReport);
  801. begin
  802.   Reports.Add(Value);
  803.   Value.FOwner := Self;
  804.   FUpdated := False;
  805. end;
  806.  
  807. procedure TReportManager.Remove(Value: TReport);
  808. begin
  809.   with Reports do Delete(IndexOf(Value));
  810.   Value.FOwner := nil;
  811.   FUpdated := False;
  812. end;
  813.  
  814. procedure TReportManager.AddDataSet(Root: TComponent);
  815. var
  816.   I: Integer;
  817. begin
  818.   if Root is TDataSet then FDataSets.Add(Root);
  819.   for I := 0 to Root.ComponentCount - 1 do
  820.     AddDataSet(Root.Components[I]);
  821. end;
  822.  
  823. function TReportManager.GetDataSet(Index: Integer): TDataSet;
  824. begin
  825.   Result := DataSets[Index];
  826. end;
  827.  
  828. function TReportManager.GetReport(Index: Integer): TReport;
  829. begin
  830.   Result := FReports[Index];
  831. end;
  832.  
  833. procedure TReportManager.UpdateDataSets;
  834. var
  835.   I, J: Integer;
  836.   Matched: Boolean;
  837. begin
  838.   FDataSets.Clear;
  839.   for I := 0 to Reports.Count - 1 do
  840.   begin
  841.     Matched := False;
  842.     for J := I + 1 to Reports.Count - 1 do
  843.       if Report[I].Owner = Report[J].Owner then
  844.       begin
  845.         Matched := True;
  846.         Break;
  847.       end;
  848.     if not Matched then AddDataSet(Report[I].Owner);
  849.   end;
  850.   FUpdated := True;
  851. end;
  852.  
  853. function TReportManager.ExecuteSQL(ExecInfo: PExecInfo;
  854.   StartExecInfo: PStartExecInfo): Bool;
  855. var
  856.   I, Size: Integer;
  857.   S: string;
  858.   DataElement: PDataElement;
  859.   pStmtMem, pMemoMem: Pointer;
  860.  
  861.   function GetDataSize(Value: TField): Integer;
  862.   begin
  863.     case Value.DataType of
  864.       ftString: Result := Value.Size + 1;
  865.       ftSmallint, ftInteger, ftWord, ftBoolean, ftAutoInc:
  866.         Result := SizeOf(Integer);
  867.       ftFloat, ftCurrency, ftBCD:
  868.         Result := SizeOf(Double);
  869.       ftDate, ftTime, ftDateTime:
  870.         Result := SizeOf(TRSDateTime);
  871.       else Result := 0;
  872.     end;
  873.   end;
  874.  
  875. begin
  876.   Result := False;
  877.   S := StartExecInfo^.TableName;
  878.   with StmtHandles[StartExecInfo^.StmtIndex] do
  879.   begin
  880.     StmtHandle := OpenFileMapping(FILE_MAP_WRITE, False, StartExecInfo^.StmtName);
  881.     if StmtHandle <> 0 then
  882.       pStmtMem := MapViewOfFile(StmtHandle, FILE_MAP_WRITE, 0, 0, 0) else
  883.       pStmtMem := nil;
  884.     StmtMem := pStmtMem;
  885.     MemoHandle := OpenFileMapping(FILE_MAP_WRITE, False, StartExecInfo^.MemoName);
  886.     if MemoHandle <> 0 then
  887.       pMemoMem := MapViewOfFile(MemoHandle, FILE_MAP_WRITE, 0, 0, 0) else
  888.       pMemoMem := nil;
  889.     MemoMem := pMemoMem;
  890.   end;
  891.   if (StmtHandles[StartExecInfo^.StmtIndex].StmtHandle <> 0) and
  892.     (StmtHandles[StartExecInfo^.StmtIndex].MemoHandle <> 0) then
  893.     with ExecInfo^ do
  894.     begin
  895.       DataSet := GetDataSetByName(S);
  896.       if DataSet <> nil then
  897.       try
  898.         if DataSet.Active then DataSet.First
  899.         else DataSet.Open;
  900.         MoreRecords := not DataSet.EOF;
  901.         NumCols := 0;
  902.         DataElement := PDataElement(pStmtMem);
  903.         Size := 0;
  904.         for I := 0 to DataSet.FieldCount - 1 do
  905.           Inc(Size, GetDataSize(DataSet.Fields[I]) + SizeOf(TDataElement));
  906.         if Size < StatementBuffer then
  907.         begin
  908.           for I := 0 to DataSet.FieldCount - 1 do
  909.             with DataSet.Fields[I], DataElement^ do
  910.               if ValidDataType(DataType) then
  911.               begin
  912.                 StrLCopy(ColumnName, PChar(FieldName), SizeOf(ColumnName) - 1);
  913.                 FieldType := Ord(DataType);
  914.                 FieldLength := GetDataSize(DataSet.Fields[I]);
  915.                 Inc(Integer(DataElement), SizeOf(TDataElement) + FieldLength);
  916.                 Inc(NumCols);
  917.               end;
  918.           Result := GetData(DataSet, pStmtMem);
  919.         end
  920.         else FLastError := SRptBindBuffer;
  921.       except
  922.         on E: Exception do
  923.           FLastError := E.Message;
  924.       end
  925.       else FLastError := SRptDataSetNotAvailable;
  926.     end
  927.   else FLastError := SRptSharedMemoryError;
  928. end;
  929.  
  930. function TReportManager.GetData(DataSet: TDataSet; pStmtMem: PDataElement): Bool;
  931. var
  932.   I: Integer;
  933.   DataValue: Pointer;
  934.   DataElement: PDataElement;
  935. begin
  936.   Result := True;
  937.   try
  938.     DataElement := pStmtMem;
  939.     for I := 0 to DataSet.FieldCount - 1 do
  940.       with DataSet.Fields[I], DataElement^ do
  941.         if ValidDataType(DataType) then
  942.         begin
  943.           DataValue := Pointer(@DataElement^.Data);
  944.           Null := IsNull;
  945.           if not Null then
  946.           begin
  947.             case DataType of
  948.               ftString, ftVarBytes:
  949.                 StrCopy(PChar(DataValue), PChar(AsString));
  950.               ftBoolean: Bool(DataValue^) := AsBoolean;
  951.               ftSmallint, ftInteger, ftWord, ftAutoInc:
  952.                 Integer(DataValue^) := AsInteger;
  953.               ftFloat, ftCurrency, ftBCD:
  954.                 Double(DataValue^) := AsFloat;
  955.               ftDate, ftTime, ftDateTime:
  956.                 GetDecodedDateTime(AsDateTime, TRSDateTime(DataValue^));
  957.             end;
  958.           end;
  959.           Inc(Integer(DataElement), SizeOf(TDataElement) + FieldLength);
  960.         end;
  961.   except
  962.     on E: Exception do
  963.       begin
  964.         FLastError := E.Message;
  965.         Result := False;
  966.       end;
  967.   end;
  968. end;
  969.  
  970. function TReportManager.GetNext(SQLStruct: PSQLStruct; var MoreData: Bool): Bool;
  971. var
  972.   pStmtMem: Pointer;
  973.   DataSet: TDataSet;
  974. begin
  975.   Result := False;
  976.   pStmtMem := StmtHandles[SQLStruct^.Index].StmtMem;
  977.   DataSet := SQLStruct^.DataSet;
  978.   if DataSet <> nil then
  979.     try
  980.       DataSet.Next;
  981.       Result := GetData(DataSet, pStmtMem);
  982.       MoreData := not DataSet.EOF;
  983.     except
  984.       on E: Exception do
  985.         FLastError := E.Message;
  986.     end
  987.   else FLastError := SRptNoDataSetAvailable;
  988. end;
  989.  
  990. function TReportManager.GetMemo(MemoStruct: PMemoStruct): Bool;
  991. var
  992.   MemoMem: Pointer;
  993.   DataSet: TDataSet;
  994.   S: string;
  995. begin
  996.   Result := False;
  997.   MemoMem := StmtHandles[MemoStruct^.Index].MemoMem;
  998.   PChar(MemoMem)^ := #0;
  999.   DataSet := MemoStruct^.DataSet;
  1000.   if DataSet <> nil then
  1001.     try
  1002.       S := DataSet.FieldByName(MemoStruct^.ColumnName).AsString;
  1003.       if Length(S) >= MemoStruct^.Pos then
  1004.         StrLCopy(MemoMem, @S[MemoStruct^.Pos + 1], MemoBuffer - 1);
  1005.       Result := True;
  1006.     except
  1007.       on E: Exception do
  1008.         FLastError := E.Message;
  1009.     end
  1010.   else FLastError := SRptNoDataSetAvailable;
  1011. end;
  1012.  
  1013. function TReportManager.ValidDataType(Value: TFieldType): Boolean;
  1014. begin
  1015.   Result := not (Value in [ftUnknown, ftBytes, ftVarBytes,
  1016.     ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle, ftTypedBinary])
  1017. end;
  1018.  
  1019. function TReportManager.EndSQL(SQLStruct: PSQLStruct): Bool;
  1020. begin
  1021.   Result := True;
  1022.   if SQLStruct^.DataSet <> nil then
  1023.   try
  1024.     SQLStruct^.DataSet.Close;
  1025.     CleanUpStmt(StmtHandles[SQLStruct^.Index]);
  1026.   except
  1027.     on E: Exception do
  1028.     begin
  1029.       FLastError := E.Message;
  1030.       Result := False;
  1031.     end;
  1032.   end
  1033. end;
  1034.  
  1035. function TReportManager.GetDataSets: TList;
  1036. begin
  1037.   if not Updated then UpdateDataSets;
  1038.   Result := FDataSets;
  1039. end;
  1040.  
  1041. procedure TReportManager.GetTableList(Buffer: PChar);
  1042. var
  1043.   S: string;
  1044.   I: Integer;
  1045. begin
  1046.   Buffer^ := #0;
  1047.   for I := 0 to DataSets.Count - 1 do
  1048.   begin
  1049.     S := DataSet[I].Name;
  1050.     StrCopy(Buffer, PChar(S));
  1051.     Inc(Integer(Buffer), Length(S) + 1);
  1052.   end;
  1053.   Buffer^ := #0;
  1054. end;
  1055.  
  1056. function TReportManager.GetDataSetByName(Value: string): TDataSet;
  1057. var
  1058.   I: Integer;
  1059. begin
  1060.   Result := nil;
  1061.   for I := 0 to DataSets.Count - 1 do
  1062.     if DataSet[I].Name = Value then
  1063.     begin
  1064.       Result := DataSet[I];
  1065.       Break;
  1066.     end;
  1067. end;
  1068.  
  1069. function TReportManager.GetColumnList(Buffer: PChar): Bool;
  1070. var
  1071.   S: string;
  1072.   DataSet: TDataSet;
  1073.  
  1074.   procedure GetNamesByField;
  1075.   var
  1076.     I: Integer;
  1077.   begin
  1078.     for I := 0 to DataSet.FieldCount - 1 do
  1079.       if ValidDataType(DataSet.Fields[I].DataType) then
  1080.       begin
  1081.         S := DataSet.Fields[I].FieldName;
  1082.         StrCopy(Buffer, PChar(S));
  1083.         Inc(Integer(Buffer), Length(S) + 1);
  1084.       end;
  1085.   end;
  1086.  
  1087.   procedure GetNamesByFieldDef;
  1088.   var
  1089.     I: Integer;
  1090.   begin
  1091.     for I := 0 to DataSet.FieldDefs.Count - 1 do
  1092.       if ValidDataType(DataSet.FieldDefs[I].DataType) then
  1093.       begin
  1094.         S := DataSet.FieldDefs[I].Name;
  1095.         StrCopy(Buffer, PChar(S));
  1096.         Inc(Integer(Buffer), Length(S) + 1);
  1097.       end;
  1098.   end;
  1099.  
  1100. begin
  1101.   Result := True;
  1102.   S := Buffer;
  1103.   Buffer^ := #0;
  1104.   DataSet := GetDataSetByName(S);
  1105.   if DataSet <> nil then
  1106.     with DataSet do
  1107.     try
  1108.       FieldDefs.Update;
  1109.       if FieldCount <> 0 then
  1110.         GetNamesByField else
  1111.         GetNamesByFieldDef;
  1112.     except
  1113.       on E: Exception do
  1114.         begin
  1115.           FLastError := E.Message;
  1116.           Result := False;
  1117.         end;
  1118.     end
  1119.   else begin
  1120.     FLastError := SRptNoDataSetAvailable;
  1121.     Result := False;
  1122.   end;
  1123.   Buffer^ := #0;
  1124. end;
  1125.  
  1126. procedure ProcessRequest;
  1127. var
  1128.   pData: Pointer;
  1129.   CallRec: PCallInfo;
  1130. begin
  1131.   CallRec := PCallInfo(SharedMem);
  1132.   pData := @CallRec^.Data;
  1133.   if (CallRec^.CallType = ctDesignId) and
  1134.     (ReportManager.Reports.Count > 0) and
  1135.     (csDesigning in ReportManager.Report[0].ComponentState) then
  1136.   begin
  1137.     CallRec^.ErrorCode := False;
  1138.     DWORD(pData^) := ProcessId;
  1139.   end
  1140.   else if CallRec^.ProcessId = ProcessId then
  1141.     SendMessage(ReportManager.Handle, $7F00, 0, 0);
  1142.   ResetEvent(StartEvent);
  1143.   SetEvent(SyncEvent);
  1144. end;
  1145.  
  1146. function WaitForRequest(pData: Pointer): Integer; stdcall;
  1147. begin
  1148.   Result := 0;
  1149.   while True do
  1150.   begin
  1151.     Result := WaitForSingleObject(StartEvent, INFINITE);
  1152.     if Result = WAIT_OBJECT_0 then ProcessRequest
  1153.     else break;
  1154.   end;
  1155. end;
  1156.  
  1157. procedure Initialize;
  1158. begin
  1159.   ReportManager := TReportManager.Create(nil);
  1160.   ProcessId := GetCurrentProcessId;
  1161.   if InitDriver then
  1162.     InitObjects(StartEvent, SyncEvent, SharedMem, @WaitForRequest);
  1163.   if InitAPIDriver then
  1164.     RS_RegisterCallBack(@AsyncCallback);
  1165. end;
  1166.  
  1167. procedure Finalize;
  1168. var
  1169.   Thread: THandle;
  1170.   I: Integer;
  1171. begin
  1172.   for I := Low(StmtHandles) to High(StmtHandles) do
  1173.     CleanUpStmt(StmtHandles[I]);
  1174.   if @GetThread <> nil then
  1175.   begin
  1176.     Thread := GetThread;
  1177.     if Thread <> 0 then TerminateThread(Thread, 0);
  1178.   end;
  1179.   ReportManager.Free;
  1180.   if DriverLoaded then FreeLibrary(DriverHandle);
  1181.   if APIDriverLoaded then FreeLibrary(APIDriverHandle);
  1182. end;
  1183.  
  1184. initialization
  1185.   Initialize;
  1186. finalization
  1187.   Finalize;
  1188. end.
  1189.